package cs5620.object;

import javax.vecmath.*;
import javax.vecmath.Quat4f;

import cs5620.material.IsotropicWard;
import cs5620.material.CookTorrance;
import cs5620.material.Lambertian;
import cs5620.material.Phong;

public class Helicopter implements ParameterizedObjectMaker {
	/**
	 * Helicopter takes exactly 8 parameters, each of them having to be a Number object
	 * The parameters are as follows:
	 * Helicopter length
	 * Helicopter width
	 * Helicopter height
	 * Tailrotor Blade Amount
	 * Tailrotor Blade Length
	 * Rotor Blade Amount
	 * Rotor Blade Length
	 * Counter Blade Amount
	 */
	public final HierarchicalObject make(Object... inputs) {
		Scene out = new Scene();
		double[] params = null;
		//Validate inputs
		boolean hasParams = false;
		if (inputs.length == 8) {
			hasParams = true;
			params = new double[8];
			int index = 0;
			for (Object input : inputs) {
				params[index] = ((Number)input).doubleValue();
			}
		}

		//default body color
		IsotropicWard bodyColor = new IsotropicWard(new Color3f(0.8f, 0.8f, 0.6f),
				new Color3f(1.0f, 1.0f, 0.0f), 0.4f);
		//default blade color
		CookTorrance bladeColor = new CookTorrance(new Color3f(0.9f,0.9f,1.0f),
				new Color3f(0.8f,0.8f,1.0f),1.0f,30.0f);
		//default ski color
		Phong skiColor = new Phong(new Color3f(0.0f,0.0f,0.0f),new Color3f(1.0f,1.0f,1.0f),5.0f);
		//default leg prong color
		IsotropicWard legsColor = new IsotropicWard(new Color3f(0.3f,0.2f,0.1f), new Color3f(0.5f,0.5f,0.3f),0.4f);
		
		
		Group helicopter = new Group("Helicopter");
		helicopter.setTranslate(0.0f,0.0f,-0.2f);
		helicopter.setScale(0.7f,0.7f,0.7f);
		
		if (hasParams) {
			float lengthScale = (float)params[0]/1.8f;
			float widthScale = (float)params[1]/0.5f;
			float heightScale = (float)params[2]/0.7f;
			
			helicopter.setScale(0.7f*widthScale, 0.7f*heightScale, 0.7f*lengthScale);
		}
		
		//heli body
		Group core = new Group("Core");
		MeshObject body = PrimitiveFactory.makeSphere(5, 8, "Body");
		body.setScale(0.3f,0.3f,0.6f);
		
		//heli skis
		Group legs = new Group("Legs");
		
		//left heli ski
		Group leftLeg = new Group("LeftLeg");
		MeshObject leftStrut1 = PrimitiveFactory.makeCylinder(1,20,"LeftStrut1");
		MeshObject leftStrut2 = PrimitiveFactory.makeCylinder(1,20,"LeftStrut2");
		MeshObject leftSki = PrimitiveFactory.makeCylinder(1,20,"LeftSki");
		
		Vector3f strutScale = new Vector3f(0.03f,0.1f,0.03f);
		Vector3f skiScale = new Vector3f(0.05f,0.4f,0.05f);
		leftStrut1.setScale(strutScale);
		leftStrut2.setScale(strutScale);
		leftSki.setScale(skiScale);
		
		AxisAngle4f leftStrutRot = new AxisAngle4f(0.0f,0.0f,-1.0f,(float)Math.PI/5.0f);
		leftStrut1.setRotationAxisAngle(leftStrutRot);
		leftStrut2.setRotationAxisAngle(leftStrutRot);
		leftSki.setRotationAxisAngle(90, -1.0f, 0f, 0f);
		
		leftStrut1.setTranslate(-0.25f, -.25f, -0.2f);
		leftStrut2.setTranslate(-0.25f, -.25f, 0.2f);
		leftSki.setTranslate(-0.30f, -.32f, 0.0f);
		
		leftLeg.addObject(leftStrut1);
		leftLeg.addObject(leftStrut2);
		leftLeg.addObject(leftSki);
		
		legs.addObject(leftLeg);
		
		//right heli ski
		Group rightLeg = new Group("RightLeg");
		MeshObject rightStrut1 = PrimitiveFactory.makeCylinder(1,20,"RightStrut1");
		MeshObject rightStrut2 = PrimitiveFactory.makeCylinder(1,20,"RightStrut2");
		MeshObject rightSki = PrimitiveFactory.makeCylinder(1,20,"RightSki");
		
		rightStrut1.setScale(strutScale);
		rightStrut2.setScale(strutScale);
		rightSki.setScale(skiScale);
		
		AxisAngle4f rightStrutRot = new AxisAngle4f(0.0f,0.0f,-1.0f,2.0f*(float)Math.PI - (float)Math.PI/5.0f);
		rightStrut1.setRotationAxisAngle(rightStrutRot);
		rightStrut2.setRotationAxisAngle(rightStrutRot);
		rightSki.setRotationAxisAngle(90, -1.0f, 0f, 0f);
		
		rightStrut1.setTranslate(0.25f, -.25f, -0.2f);
		rightStrut2.setTranslate(0.25f, -.25f, 0.2f);
		rightSki.setTranslate(0.30f, -.32f, 0.0f);
		
		rightLeg.addObject(rightStrut1);
		rightLeg.addObject(rightStrut2);
		rightLeg.addObject(rightSki);
		
		legs.addObject(rightLeg);
		core.addObject(legs);
		
		//cockpit seals
		Group cockpitSeal = new Group("CockpitSeal");
		MeshObject cockpitSeal1 = PrimitiveFactory.makeCylinder(1, 20, "cockpitSeal1");
		cockpitSeal1.setScale(0.19f,0.01f,0.20f);
		cockpitSeal1.setTranslate(0.0f,0.05f,-0.5f);
		cockpitSeal1.setRotationAxisAngle(90, 1.0f,0.0f,0.0f);
		MeshObject cockpitSeal2 = PrimitiveFactory.makeCylinder(1, 20, "cockpitSeal2");
		cockpitSeal2.setScale(0.15f,0.01f,0.18f);
		cockpitSeal2.setTranslate(0.0f,0.02f,-0.65f);
		cockpitSeal2.setRotationAxisAngle(90, 1.0f,0.0f,0.0f);
		MeshObject cockpitSeal3 = PrimitiveFactory.makeCylinder(1, 20, "cockpitSeal3");
		
		//Transforms for vertical cockpit divider
		Quat4f rotate1 = new Quat4f();
		Quat4f rotate2 = new Quat4f();
		AxisAngle4f rotate1axis = new AxisAngle4f(0.0f,0.0f,1.0f,(float)Math.PI/2.0f);
		AxisAngle4f rotate2axis = new AxisAngle4f(1.0f,0.0f,0.0f,-(float)Math.PI/30.0f);
		rotate1.set(rotate1axis);
		rotate2.set(rotate2axis);
		rotate2.mul(rotate1);
		
		cockpitSeal3.setScale(0.2f,0.01f,0.4f);
		cockpitSeal3.setTranslate(0.0f,0.05f,-0.45f);
		cockpitSeal3.setRotation(rotate2);
		
		cockpitSeal.addObject(cockpitSeal1);
		cockpitSeal.addObject(cockpitSeal2);
		cockpitSeal.addObject(cockpitSeal3);
		
		//cockpit core
		Group cockpitCore = new Group("CockpitCore");
		
		MeshObject cockpit = PrimitiveFactory.makeSphere(4, 10, "Cockpit");
		cockpit.setScale(0.2f,0.26f,0.6f);
		cockpit.setTranslate(0.0f,0.0f,-0.27f);
		cockpit.setMaterial(new IsotropicWard(new Color3f(0.4f, 0.5f, 0.9f),
				new Color3f(0.6f, 0.8f, 0.6f), 0.1f));
		MeshObject cockpitBottom = PrimitiveFactory.makeSphere(3, 6, "CockpitBottom");
		cockpitBottom.setScale(0.23f,0.15f,0.6f);
		cockpitBottom.setTranslate(0.0f,-0.1f,-0.34f);

		cockpitCore.addObject(cockpit);
		cockpitCore.addObject(cockpitSeal);
		cockpitCore.addObject(cockpitBottom);
		
		//tail
		Group tail = new Group("Tail");
		MeshObject attach = PrimitiveFactory.makeCylinder(3, 6, "TailAttach");
		attach.setScale(0.2f,0.4f,0.2f);
		attach.setTranslate(0f,0.1f,0.15f);
		attach.setRotationAxisAngle(-90, 1.0f, 0f, 0f);
		
		MeshObject tailBoom = PrimitiveFactory.makeCylinder(3, 6, "TailBoom");
		tailBoom.setScale(0.1f,0.35f,0.1f);
		tailBoom.setTranslate(0f,0.15f,0.7f);
		tailBoom.setRotationAxisAngle(-90,1.0f,0f,0f);
		
		MeshObject tailBoom2 = PrimitiveFactory.makeCylinder(3,6, "TailBoom2");
		tailBoom2.setScale(0.05f,0.5f,0.07f);
		tailBoom2.setTranslate(0f,0.2f,1f);
		tailBoom2.setRotationAxisAngle(-90,1.0f,0f,0f);
		
		MeshObject tailBoomEnd = PrimitiveFactory.makeCylinder(2,8, "TailBoomEnd");
		tailBoomEnd.setScale(0.1f,0.05f,0.1f);
		tailBoomEnd.setTranslate(0f,0.18f,1.5f);
		tailBoomEnd.setRotationAxisAngle(90,0.0f,0.0f,1.0f);
		
		//tail rotor
		Group tailRotor = new Group("TailRotor");
		MeshObject tailRotorMount = PrimitiveFactory.makeCylinder(2,8, "TailRotorMount");
		tailRotorMount.setScale(0.05f,0.05f,0.05f);
		tailRotorMount.setTranslate(-0.05f,0.18f,1.5f);
		tailRotorMount.setRotationAxisAngle(90,0.0f,0.0f,1.0f);
		
		AxisAngle4f rotorRot;
		AxisAngle4f bladeRot = new AxisAngle4f(1.0f,0.0f,0.0f,(float)Math.PI/8);
		Quat4f tempRot = new Quat4f();
		tempRot.set(bladeRot);
		Quat4f totalRot = new Quat4f();
		float xcoord, zcoord,rotate;
		
		//Creating tailblades
		int tailBlades = 5; 
		float tailBladeLength = 0.1f; 
		
		if (hasParams) {
			tailBlades = (int)params[3];
			tailBladeLength = (float)params[4];
		}
		
		Group tailRotorBlades = new Group("TailRotorBlades");
		tailRotorBlades.setRotationAxisAngle(90, 0f, 0f, 1.0f);
		tailRotorBlades.setTranslate(-0.07f,0.18f,1.5f);
		
		MeshObject tailBlade;
		for (int i = 0; i < tailBlades; i++) {
			tailBlade = PrimitiveFactory.makeBox("TailBlade" + i);
			tailBlade.setScale(tailBladeLength,0.005f,0.03f);
			xcoord = tailBladeLength*(float)Math.cos((double)2*i*Math.PI/tailBlades);
			zcoord = tailBladeLength*(float)Math.sin((double)2*i*Math.PI/tailBlades);
			
			rotate = (float)i * ((float)360/(float)tailBlades);
			rotorRot = new AxisAngle4f(0.0f, -1.0f, 0.0f, (float)(rotate*2.0*Math.PI)/360f);
			totalRot.set(rotorRot);		
			totalRot.mul(tempRot);
			tailBlade.setRotation(totalRot);
			
			tailBlade.setTranslate(xcoord,0.0f,zcoord);
			
			tailRotorBlades.addObject(tailBlade);
		}
		
		tailRotor.addObject(tailRotorMount);
		tailRotor.addObject(tailRotorBlades);
		
		tail.addObject(attach);
		tail.addObject(tailBoom);
		tail.addObject(tailBoom2);
		tail.addObject(tailBoomEnd);
		tail.addObject(tailRotor);
		
		core.addObject(body);
		core.addObject(cockpitCore);
		core.addObject(tail);
		
		//rotor
		Group rotor = new Group("Rotor");
		rotor.setTranslate(0.0f,0.0f,-0.1f);
		MeshObject engineBlock = PrimitiveFactory.makeBox("EngineBlock");
		engineBlock.setScale(0.1f,0.1f,0.2f);
		engineBlock.setTranslate(0.0f,0.2f,-0.0f);
		
		MeshObject rotorShaft = PrimitiveFactory.makeCylinder(1, 20, "RotorShaft");
		rotorShaft.setScale(0.09f,0.1f,0.09f);
		rotorShaft.setTranslate(0.0f,0.3f,0.0f);
		
		Group allBlades = new Group("BladeComplex");
		allBlades.setTranslate(0.0f,0.05f,0.0f);
		
		Group rotorBlades = new Group("RotorBlades");
		rotorBlades.setTranslate(0.0f,0.35f,0.0f);
		
		
		//creating main rotor blades
		int blades = 3; 
		float bladeLength = 0.5f; 
		if(hasParams) {
			blades = (int)params[5];
			bladeLength = (float)params[6];
		}
		MeshObject rotorBlade;
		
		for (int i = 0; i < blades; i++) {
			rotorBlade = PrimitiveFactory.makeBox("RotorBlade" + i);
			rotorBlade.setScale(bladeLength,0.005f,0.05f);
			xcoord = bladeLength*(float)Math.cos((double)2*i*Math.PI/blades);
			zcoord = bladeLength*(float)Math.sin((double)2*i*Math.PI/blades);
			rotate = (float)i * ((float)360/(float)blades);
			
			rotorBlade.setTranslate(xcoord,0.0f,zcoord);
			
			rotorRot = new AxisAngle4f(0.0f, -1.0f, 0.0f, (float)(rotate*2.0*Math.PI)/360f);
			totalRot.set(rotorRot);
			
			totalRot.mul(tempRot);
			
			rotorBlade.setRotation(totalRot);
			
			rotorBlades.addObject(rotorBlade);
		}
		
		//creating smaller rotor blades
		Group counterBlades = new Group("CounterBlades");
		counterBlades.setTranslate(0.0f,0.34f,0.0f);
		int cBlades = 3;
		if(hasParams) {
			cBlades = (int)params[7];
		}
		
		MeshObject counterBlade;
		for (int i = 0; i < cBlades; i++) {
			counterBlade = PrimitiveFactory.makeBox("CounterBlade" + i);
			counterBlade.setScale(bladeLength/2.0f,0.005f,0.05f);
			xcoord = (bladeLength/4.0f)*(float)Math.cos((double)2*(i)*Math.PI/cBlades + (double)Math.PI/cBlades);
			zcoord = (bladeLength/4.0f)*(float)Math.sin((double)2*(i)*Math.PI/cBlades + (double)Math.PI/cBlades);
			counterBlade.setTranslate(xcoord,0.0f,zcoord);
			
			rotate = (float)(i) * ((float)360/(float)cBlades) + (360f/(float)cBlades)/2;
			
			rotorRot = new AxisAngle4f(0.0f, -1.0f, 0.0f, (float)(rotate*2.0*Math.PI)/360f);
			totalRot.set(rotorRot);
			
			totalRot.mul(tempRot);
			counterBlade.setRotation(totalRot);
			
			counterBlades.addObject(counterBlade);
		}
		
		//Setting up colors
		body.setMaterial(bodyColor);
		tail.setMaterial(bodyColor);
		cockpitBottom.setMaterial(bodyColor);
		engineBlock.setMaterial(new Phong(new Color3f(0.0f,0.0f,0.0f),new Color3f(1.0f,1.0f,1.0f),10.0f));
		tailRotor.setMaterial(bladeColor);
		cockpitSeal.setMaterial(new Phong(new Color3f(0.0f,0.0f,0.1f),new Color3f(1.0f,1.0f,1.0f),4.0f));
		allBlades.setMaterial(bladeColor);
		legs.setMaterial(legsColor);
		leftSki.setMaterial(skiColor);
		rightSki.setMaterial(skiColor);
		
		allBlades.addObject(rotorBlades);
		allBlades.addObject(counterBlades);
		allBlades.addObject(rotorShaft);
		rotorShaft.setMaterial(new CookTorrance(new Color3f(0.1f,0.1f,0.1f), new Color3f(0.8f,0.8f,1.0f),
				0.8f,30.0f));
		
		rotor.addObject(engineBlock);
		rotor.addObject(allBlades);
		
		helicopter.addObject(core);
		helicopter.addObject(rotor);
		out.addObject(helicopter);
		return out;
	}
}
